package com.hq.tomato.ms.config;

import com.hq.tomato.ms.context.StrategyApplication;
import com.hq.tomato.ms.dlq.DispatcherDlq;
import com.hq.tomato.ms.strategy.DiscardDlqStrategy;
import com.hq.tomato.ms.strategy.RequeueDlqStrategy;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitBinderConfigurationProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitBindingProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitConsumerProperties;
import org.springframework.cloud.stream.binder.rabbit.properties.RabbitExtendedBindingProperties;
import org.springframework.cloud.stream.config.BindingProperties;
import org.springframework.cloud.stream.config.BindingServiceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 *
 *
 * @Description: <br>
 * @Project: <br>
 * @CreateDate: Created in 2019-10-09 10:57 <br>
 * @Author: <a href="1026365768@qq.com">qiang.hu</a>
 */
@EnableConfigurationProperties({ RabbitBinderConfigurationProperties.class, RabbitExtendedBindingProperties.class, BindingServiceProperties.class })
public class TomatoMsAutoConfiguration {


    /**
     * @Author huqiang
     * @Description //TODO 策略上下文
     * @Date  2019-11-20
     * @Param
     * @return com.hq.tomato.ms.context.StrategyApplication
     */
    @Bean
    public StrategyApplication strategyApplication(){
        return new StrategyApplication();
    }


    /**
     * @Author huqiang
     * @Description //TODO 重入队列策略
     * @Date  2019-11-20
     * @Param
     * @return com.hq.tomato.ms.strategy.RequeueDlqStrategy
     */
    @Bean(name = "dlq.process.requeue")
    public RequeueDlqStrategy requeueDlqStrategy(){
        return new RequeueDlqStrategy();
    }


    @Bean
    @ConditionalOnMissingBean(name = "tomatoMsProperties")
    public TomatoMsProperties tomatoMsProperties(){
        return new TomatoMsProperties();
    }

    /**
     * @Author huqiang
     * @Description //TODO 丢弃消息计入log策略
     * @Date  2019-11-20
     * @Param
     * @return com.hq.tomato.ms.strategy.DiscardDlqStrategy
     */
    @Bean(name = "dlq.process.discard")
    public DiscardDlqStrategy discardDlqStrategy(){
        return new DiscardDlqStrategy();
    }


    /**
     * @Author huqiang
     * @Description //TODO 死信队列转发器
     * @Date  2019-11-20
     * @Param
     * @return com.hq.tomato.ms.dlq.DispatcherDlq
     */
    @Bean
    public DispatcherDlq dispatcherDlq(){
        return new DispatcherDlq();
    }




    /**
     * @Author huqiang
     * @Description //TODO 动态监听死信队列监听的queue（可重写此bean）
     * @Date  2019-11-20
     * @Param connectionFactory
     * @Param dlqProcessService
     * @Param rabbitExtendedBindingProperties
     * @Param bindingServiceProperties
     * @return org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer
     */
    @Bean(name = "tomatoMsMessageListenerContainer")
    @ConditionalOnMissingBean(name = "tomatoMsMessageListenerContainer")
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory,
                                                                   DispatcherDlq dlqProcessService,
                                                                   RabbitExtendedBindingProperties rabbitExtendedBindingProperties,
                                                                   BindingServiceProperties bindingServiceProperties){
        String[] dlqQueueNames = getDlqQueueNames(rabbitExtendedBindingProperties,bindingServiceProperties);
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.setQueueNames(dlqQueueNames);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        container.setMissingQueuesFatal(false);
        container.setMessageListener(dlqProcessService);
        return container;
    }

    private String [] getDlqQueueNames(RabbitExtendedBindingProperties rabbitExtendedBindingProperties,BindingServiceProperties bindingServiceProperties){
        Map<String, RabbitBindingProperties> rabbitBindingProperties = rabbitExtendedBindingProperties.getBindings();
        Map<String, BindingProperties> streamBindingProperties = bindingServiceProperties.getBindings();

        if(CollectionUtils.isEmpty(rabbitBindingProperties)||CollectionUtils.isEmpty(streamBindingProperties)){
            return null;
        }

        Set<String> result = new HashSet<>();

        rabbitBindingProperties.forEach((k,v)->{
            RabbitConsumerProperties consumer = v.getConsumer();
            if(Objects.nonNull(consumer)){
                // 如果指定了死信队列的名字 则直接使用此队列名 否则动态去拼接死信队列的名字
                String deadLetterQueueName = consumer.getDeadLetterQueueName();
                if(StringUtils.isEmpty(deadLetterQueueName)){
                    BindingProperties bindingProperties = streamBindingProperties.get(k);
                    deadLetterQueueName = getDlqQueueNameByBinding(bindingProperties);
                }

                if(!StringUtils.isEmpty(deadLetterQueueName)){
                    result.add(deadLetterQueueName);
                }
            }
        });

        return result.toArray(new String[result.size()]);
    }


    /**
     * @Author huqiang
     * @Description //TODO 通过binding获取Dlq的名字
     * @Date  2019-11-19
     * @Param bindingProperties
     * @return java.lang.String
     */
    private String getDlqQueueNameByBinding(BindingProperties bindingProperties){
        String destination = bindingProperties.getDestination();
        String group = bindingProperties.getGroup();
        String dlqQueueName = null;
        StringBuilder stringBuilder = new StringBuilder();
        if(!StringUtils.isEmpty(destination)){
            stringBuilder.append(destination);
        }

        if(!StringUtils.isEmpty(group)){
            if(stringBuilder.length()>0){
                stringBuilder.append(".");
            }
            stringBuilder.append(group);
        }
        dlqQueueName = stringBuilder.append(".dlq").toString();

        return dlqQueueName;
    }
}
